package cn.jfast.framework.jdbc.orm;

import cn.jfast.framework.jdbc.annotation.*;
import cn.jfast.framework.jdbc.orm.sql.*;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

public class DaoProxy implements MethodInterceptor {
    private Object daoProxy;
    private ClassPool cp = ClassPool.getDefault();
    private CtClass clazz;
    private CtMethod ctMethod;
    private Type[] paramTypes;
    private String[] paramNames;
    private Select select;
    private Update update;
    private Insert insert;
    private Delete delete;
    private Type returnType;
    private Executor sql;

    public Object getInstance(Object dao){
        this.daoProxy = dao;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.daoProxy.getClass());
        enhancer.setCallback(this);
        cp.insertClassPath(new ClassClassPath((daoProxy.getClass())));
        try {
            clazz = cp.getCtClass(daoProxy.getClass().getName());
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(method.isAnnotationPresent(Body.class)
                ||daoProxy.getClass().isAnnotationPresent(Body.class))
            return method.invoke(daoProxy,objects);
        ctMethod = clazz.getDeclaredMethod(method.getName());
        paramNames = getMethodParamNames(ctMethod);
        paramTypes = method.getParameterTypes();
        returnType = method.getGenericReturnType();
        if(method.isAnnotationPresent(Select.class)) {
            select = method.getAnnotation(Select.class);
            sql = new SelectSql(paramTypes,paramNames,objects,select,returnType,daoProxy,method);
        } else if(method.isAnnotationPresent(Update.class)) {
            update = method.getAnnotation(Update.class);
            sql = new UpdateSql(paramTypes,paramNames,objects,update,returnType,daoProxy,method);
        } else if(method.isAnnotationPresent(Insert.class)) {
            insert = method.getAnnotation(Insert.class);
            sql = new InsertSql(paramTypes,paramNames,objects,insert,returnType,daoProxy,method);
        } else if(method.isAnnotationPresent(Delete.class)) {
            delete = method.getAnnotation(Delete.class);
            sql = new DeleteSql(paramTypes,paramNames,objects,delete,returnType,daoProxy,method);
        } else {
            sql = new MethodSql(paramTypes,paramNames,objects,method.getName(),returnType,daoProxy,method);
        }
        Object result = sql.execute();
        return result;

    }

    protected static String[] getMethodParamNames(CtMethod cm) {
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
                .getAttribute(LocalVariableAttribute.tag);
        String[] paramNames = null;
        try {
            paramNames = new String[cm.getParameterTypes().length];
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramNames.length; i++) {
            paramNames[i] = attr.variableName(i + pos);
        }
        return paramNames;

    }

}
